home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / Pascal / Applications / NIH Image 1.59 / 1.59 Source / Registration.p < prev    next >
Encoding:
Text File  |  1995-07-24  |  29.0 KB  |  633 lines  |  [TEXT/PJMM]

  1. unit Registration;
  2. {************************************************************************}
  3. {*     Registration.p                                                                                                                                       *}
  4. {*     by Michael Castle                                                                                                                                  *}
  5. {*     University of Michigan Mental Health Research Institute (MHRI)                                                     *}
  6. {*     e-mail address: mike.castle@umich.edu                                                                                       *}
  7. {*     last modified on 11 March 1994                                                                                                          *}
  8. {************************************************************************}
  9.  
  10. interface
  11.  
  12.     uses
  13.         Memory, QuickDraw, Packages, Menus, Events, Fonts, Scrap, ToolUtils,Resources, Errors, Palettes, Globals, Utilities, File2, File1, Graphics, Camera, Text, Filters, Stacks;
  14.  
  15.  
  16.     procedure DoRegister;
  17.  
  18.  
  19. implementation
  20.  
  21.     const
  22.  
  23.         RegisterImagesID = 129;                         {Dialog IDs}
  24.         FiducialsOnScreenID = 3;
  25.         FiducialsFromFileID = 4;
  26.         ConfirmFidClicksID = 5;
  27.  
  28.         biggestFid = 9999;                                    {maximum allowable fiducial stage coordinate}
  29.         MaxFids = 12;                                           {maximum number of fiducial points per slice}
  30.         MaxRegSlices = 250;
  31.  
  32.     type
  33.         RegisterRealArray = array[1..MaxRegSlices] of real;
  34.         RealPoint = record
  35.                 x: real;
  36.                 y: real;
  37.             end;     {record}
  38.         FidArray = array[1..MaxRegSlices, 1..MaxFids] of RealPoint;
  39.  
  40.  
  41. {******************************************************************************}
  42. {*     RotateAboutXY rotates the point (x,y) counterclockwise by 'angle' radians about the point (xcenter,           *}
  43. {*  ycenter).                                                                                                                                                                *}
  44. {******************************************************************************}
  45.     procedure RotateAboutXY (var x, y: extended; angle: extended; xcenter, ycenter: extended);
  46.         var
  47.             x0, y0: extended;
  48.             SinAngle, CosAngle: extended;
  49.     begin
  50.         x0 := x;
  51.         y0 := y;
  52.         CosAngle := cos(angle);
  53.         SinAngle := sin(angle);
  54.         x := (x0 - xcenter) * CosAngle - (y0 - ycenter) * SinAngle + xcenter;
  55.         y := (y0 - ycenter) * CosAngle + (x0 - xcenter) * SinAngle + ycenter;
  56.     end;     {RotateAboutXY}
  57.  
  58.  
  59. {******************************************************************************}
  60. {*     Read from a file the fiducial data necessary to register a set of images.  The data file contains several lines *}
  61. {*  of coordinates delimited by tabs (x-coordinate followed by y-coordinate in all cases).  The first line of the   *}
  62. {*  file should hold the coordinates of the Image Center point, the location (in screen coordinates) of the             *}
  63. {*  microscope crosshairs as they appear on the screen during image capture.  The second line of the file should *}
  64. {*  give the location in screen coordinates of two fixed points in an image (under the camera and microscope       *}
  65. {*  conditions selected for capture of the set of images to be aligned).  The third line of the file should provide    *}
  66. {*  the location of these two fixed points in microscope stage coordinates.  Each subsequent line in the file           *}
  67. {*  should contain (in stage coordinates) the locations of the Image Center and at least two fiducial points for an  *}
  68. {*  image in the set to be registered.  Each image must be represented by the same number of fiducial points in   *}
  69. {*  the data file.  Where fiducial coordinates are unavailable, coordinates of biggestFid+1 should appear.  No      *}
  70. {*  more than MaxFids fiducial points are allowed for each image.                                                                           *}
  71. {******************************************************************************}
  72.     function GetFiducialDataFromFile (var xcscreen, ycscreen, xscreen1, yscreen1, xscreen2, yscreen2: integer; var xstage1, ystage1, xstage2, ystage2: extended; var fiducials: FidArray; var xc, yc: RegisterRealArray): boolean;
  73.         var
  74.             fiducialfname, str: str255;
  75.             RefNum, nValues, i, j, nImages: integer;
  76.             rLine: RealLine;
  77.     begin
  78.         nImages := info^.StackInfo^.nSlices;
  79.         GetFiducialDataFromFile := FALSE;
  80.         ShowMessage('Please open a fiducial data file.');
  81.         if not GetTextFile(fiducialfname, RefNum) then
  82.             exit(GetFiducialDataFromFile);
  83.         InitTextInput(fiducialfname, RefNum);
  84.         GetLineFromText(rLine, nValues);
  85.         if (nValues <> 2) then begin
  86.                 PutError('Expecting screen coordinates of Image Center point in line 1.  Please edit fiducial data file and try again.');
  87.                 exit(GetFiducialDataFromFile);
  88.             end;
  89.         xcscreen := round(rLine[1]);
  90.         ycscreen := round(rLine[2]);
  91.         GetLineFromText(rLine, nValues);
  92.         if (nValues <> 4) then begin
  93.                 PutError('Expecting screen coordinates of two points in line 2.  Please edit fiducial data file and try again.');
  94.                 exit(GetFiducialDataFromFile);
  95.             end;
  96.         xscreen1 := round(rLine[1]);
  97.         yscreen1 := round(rLine[2]);
  98.         xscreen2 := round(rLine[3]);
  99.         yscreen2 := round(rLine[4]);
  100.         GetLineFromText(rLine, nValues);
  101.         if (nValues <> 4) then begin
  102.                 PutError('Expecting stage coordinates of two points in line 3.  Please edit fiducial data file and try again.');
  103.                 exit(GetFiducialDataFromFile);
  104.             end;
  105.         xstage1 := rLine[1];
  106.         ystage1 := rLine[2];
  107.         xstage2 := rLine[3];
  108.         ystage2 := rLine[4];
  109.         i := 1;
  110.         GetLineFromText(rLine, nValues);
  111.         while (nvalues > 0) do begin
  112.                 if nValues >= 6 then begin
  113.                         for j := 1 to (nvalues - 2) div 2 do begin
  114.                                 fiducials[i, j].x := rLine[j * 2 + 1];
  115.                                 fiducials[i, j].y := rLine[j * 2 + 2];
  116.                             end;
  117.                         for j := (nvalues - 2) div 2 + 1 to MaxFids do begin
  118.                                 fiducials[i, j].x := biggestFid + 1;
  119.                                 fiducials[i, j].y := biggestFid + 1;
  120.                             end;     {for j}
  121.                         xc[i] := rLine[1];
  122.                         yc[i] := rLine[2];
  123.                     end
  124.                 else begin
  125.                         str := StringOf('Expecting coordinates of image center and at least two fiducial points in line ', (i + 3) : 1, '.  Please edit fiducial data file and try again.');
  126.                         PutError(str);
  127.                         exit(GetFiducialDataFromFile);
  128.                     end;
  129.                 i := i + 1;
  130.                 GetLineFromText(rLine, nValues);
  131.             end;     {while}
  132.         if (i < (nImages + 1)) then begin
  133.                 if (i = nImages) then
  134.                     str := StringOf('Expecting fiducial data for one more image.  Please edit fiducial data file, then try again. ')
  135.                 else
  136.                     str := StringOf('Expecting fiducial data for ', (nImages + 1 - i) : 1, ' more slices.  Please edit fiducial data file, then try again. ');
  137.                 PutError(str);
  138.                 exit(GetFiducialDataFromFile);
  139.             end;
  140.         if (i > (nImages + 1)) then begin
  141.                 if (i = nImages + 2) then
  142.                     str := StringOf('Too much fiducial data.  Please edit fiducial data file, then try again. ')
  143.                 else
  144.                     str := StringOf('Too much fiducial data.  Please edit fiducial data file, then try again. ');
  145.                 PutError(str);
  146.                 exit(GetFiducialDataFromFile);
  147.             end;
  148.         GetFiducialDataFromFile := TRUE;
  149.     end;     {GetFiducialDataFromFile}
  150.  
  151.  
  152. {******************************************************************************}
  153. {*     Read the coordinates of a fiducial point entered on the screen by clicking once with the mouse.  Interpret    *}
  154. {*  a double-click to indicate that this is the last fiducial point for the current slice, a spacebar-click to          *}
  155. {*  to indicate that no valid fiducial exists corresponding to this fiducial in other slices (record BiggestFid+1   *}
  156. {*  as value for each coordinate), an option-click to indicate that some data for this slice has been improperly  *}
  157. {*  and the user would like to re-enter all fiducials for this slice, and command-period to indicate that the       *}
  158. {*  user wishes to cancel image registration altogether, discarding all entered fiducials coordinates.                    *}
  159. {******************************************************************************}
  160.     function GetNextFiducial (var Fidx, Fidy: integer; var done, redo: boolean): boolean;
  161.         var
  162.             pt1, pt2: point;
  163.             sbdown, optdown, DoubleClick: boolean;
  164.             MouseUpTime: LongInt;
  165.     begin
  166.         SetCursor(ToolCursor[SelectionTool]);
  167.         GetNextFiducial := FALSE;
  168.         repeat
  169.             sbdown := SpaceBarDown;
  170.             optdown := OptionKeyDown;
  171.             SetPort(info^.wptr);
  172.             GetMouse(pt1);
  173.             Show3Values(pt1.h, pt1.v, MyGetPixel(pt1.h, pt1.v));
  174.             if CommandPeriod then begin
  175.                     ShowMessage('Fiducial input cancelled.');
  176.                     exit(GetNextFiducial);
  177.                 end;     {then}
  178.         until button;
  179.         repeat
  180.         until not (button);
  181.         MouseUpTime := TickCount;
  182.         DoubleClick := FALSE;
  183.         repeat
  184.             GetMouse(pt2);
  185.             if EqualPt(pt1, pt2) then
  186.                 DoubleClick := button;
  187.         until (TickCount - MouseUpTime > GetDblTime) or DoubleClick;
  188.         if sbdown then begin
  189.                 Fidx := BiggestFid + 1;
  190.                 Fidy := BiggestFid + 1;
  191.             end
  192.         else if optdown then
  193.             redo := TRUE
  194.         else begin
  195.                 Fidx := pt1.h;
  196.                 Fidy := (Info^.nLines - 1) - pt1.v;
  197.             end;
  198.         done := DoubleClick;
  199.         while (button) do                    {clear out any buffered mouse clicks;  I don't know why there would be any}
  200.             ;                                               {such clicks, but they can be *very* disruptive while running on Quadras!}
  201.         FlushEvents(62, 0);                {make sure clicks and key presses don't linger in the event queue after exit}
  202.         GetNextFiducial := TRUE;
  203.     end;     {GetNextFiducial}
  204.  
  205.  
  206.     procedure SetSlice (i: integer);
  207.     begin
  208.         SelectSlice(i);
  209.         Info^.StackInfo^.CurrentSlice := i;
  210.         UpdateTitleBar;
  211.     end;
  212.  
  213.  
  214. {******************************************************************************}
  215. {*     Read fiducial coordinates for a set of slices to be placed in register with a reference slice (a member of   *}
  216. {*  the set).  Begin by reading fiducials for the reference slice, then read the rest.  Assign dummy values to      *}
  217. {*  variables in the fiducial data structure whose values would be used for mapping between two coordinate        *}
  218. {*  systems had fiducial data been read from a file.                                                                                                   *}
  219. {******************************************************************************}
  220.     function GetFiducialDataFromScreen (var xcscreen, ycscreen, xscreen1, yscreen1, xscreen2, yscreen2: integer; var xstage1, ystage1, xstage2, ystage2: extended; var fiducials: FidArray; var xc, yc: RegisterRealArray): boolean;
  221.         var
  222.             i, j: integer;
  223.             nImages: integer;                               {number of slices}
  224.             RefSlice: integer;                               {index of the reference slice}
  225.             Fidx, Fidy: integer;                            {coordinates of selected fiducial point}
  226.             done: boolean;                                     {done entering fiducials for this slice}
  227.             redo: boolean;                                     {re-enter fiducials for this slice}
  228.             str: str255;
  229.     begin
  230.         GetFiducialDataFromScreen := FALSE;
  231.         nImages := Info^.StackInfo^.nSlices;
  232.         xcscreen := 100;                                 {arbitrarily assign dummy image center}
  233.         ycscreen := 100;
  234.         xscreen1 := 50;                                   {assign screen and stage coords of two points}
  235.         yscreen1 := 50;                                   {so that mapping is 1:1 in x and y}
  236.         xscreen2 := 80;
  237.         yscreen2 := 80;
  238.         xstage1 := 50.0;
  239.         ystage1 := 50.0;
  240.         xstage2 := 80.0;
  241.         ystage2 := 80.0;
  242.         DrawLabels('X: ', 'Y: ', 'Value: ');        {prepare to show x,y values in results window}
  243.         RefSlice := info^.StackInfo^.CurrentSlice;
  244.         i := RefSlice;                                   {begin with reference slice}
  245.         while (i <= nImages) do begin
  246.                 done := FALSE;
  247.                 redo := FALSE;
  248.                 SetSlice(i);
  249.                 UpdatePicWindow;
  250.                 for j := 1 to MaxFids do begin
  251.                         if (not done) and (not redo) then begin
  252.                                 str := StringOf('Click on fiducial point  ', j : 1);
  253.                                 showmessage(str);
  254.                                 if not GetNextFiducial(Fidx, Fidy, done, redo) then
  255.                                     exit(GetFiducialDataFromScreen);
  256.                                 if ConfirmFidClicks then begin
  257.                                         str := StringOf('Fiducial point ', j : 1, ':  x = ', Fidx : 1, '  y = ', Fidy : 1);
  258.                                         if not (redo) then
  259.                                             while (PutMessageWithCancel(str) = cancel) do begin
  260.                                                     UpdatePicWindow;
  261.                                                     if not GetNextFiducial(Fidx, Fidy, done, redo) then
  262.                                                         exit(GetFiducialDataFromScreen);
  263.                                                     str := StringOf('Fiducial point ', j : 1, ':  x = ', Fidx : 1, '  y = ', Fidy : 1);
  264.                                                 end;     {while}
  265.                                         UpdatePicWindow;
  266.                                     end;     {then}
  267.                                 if done and (j = 1) then begin
  268.                                         PutError('You must select at least two fiducial points in each slice for registration.');
  269.                                         redo := TRUE;
  270.                                     end;     {then}
  271.                                 fiducials[i, j].x := Fidx;
  272.                                 fiducials[i, j].y := Fidy;
  273.                             end    {then}
  274.                         else begin                                  {pad rest of array with invalid fiducial data}
  275.                                 fiducials[i, j].x := biggestFid + 1;
  276.                                 fiducials[i, j].y := biggestFid + 1;
  277.                             end;     {else}
  278.                     end;     {for j}
  279.                 xc[i] := 100;
  280.                 yc[i] := 100;
  281.                 if not (redo) then begin
  282.                         if (i = RefSlice) and (RefSlice <> 1) then
  283.                             i := 1
  284.                         else if (i = RefSlice - 1) then  {don't read fiducials from reference slice twice!}
  285.                             i := i + 2
  286.                         else
  287.                             i := i + 1;
  288.                     end     {then}
  289.                 else
  290.                     PutError('Input cancelled.  Please reselect fiducial points for this slice.');
  291.             end;     {while}
  292.         GetFiducialDataFromScreen := TRUE;
  293.     end;     {GetFiducialDataFromScreen}
  294.  
  295.  
  296. {******************************************************************************}
  297. {*     Before rotating and translating an image into register, center it in a larger buffer so that rotation does      *}
  298. {*  not unnecessarily clip portions of the image that will return to view after translation.                                   *}
  299. {******************************************************************************}
  300.     procedure CenterInBigBuffer (i, picwidth, picheight, bigpicwidth, bigpicheight: integer; StackInfo: InfoPtr);
  301.         var
  302.             vloc, hOffset, vOffset: integer;
  303.             BigBufInfo: InfoPtr;
  304.             aLine: LineType;
  305.     begin
  306.         BigBufInfo := info;
  307.         info := StackInfo;
  308.         SetSlice(i);
  309.         info := BigBufInfo;
  310.         SetForegroundColor(BlackIndex);
  311.         SetBackgroundColor(WhiteIndex);
  312.         SelectAll(false);
  313.         DoOperation(EraseOp);
  314.       {write image one line at a time to center of larger buffer}
  315.         hOffset := (bigpicwidth - picwidth) div 2;
  316.         vOffset := (bigpicheight - picheight) div 2;
  317.         for vloc := 0 to picheight - 1 do begin
  318.                 info := StackInfo;
  319.                 GetLine(0, vloc, picwidth, aLine);
  320.                 info := BigBufInfo;
  321.                 PutLine(hOffset, vloc + vOffset, picwidth, aLine);
  322.             end;
  323.         UpdatePicWindow;
  324.     end;     {RegisterCenterInBigBuffer}
  325.  
  326.  
  327. {******************************************************************************}
  328. {*     After registration is complete, move the centered image back to its original window size.                             *}
  329. {******************************************************************************}
  330.     procedure TranslateBackToStack (i, xdelta, ydelta, picwidth, picheight, bigpicwidth, bigpicheight: longint; StackInfo: InfoPtr);
  331.         var
  332.             vloc, hoffset, voffset: integer;
  333.             offset: longint;
  334.             BigBufInfo: InfoPtr;
  335.             aLine: LineType;
  336.     begin
  337.         BigBufInfo := info;
  338.         info := StackInfo;
  339.         SetSlice(i);
  340.         hOffset := (bigpicwidth - picwidth) div 2 - xdelta;
  341.         vOffset := (bigpicheight - picheight) div 2 + ydelta;
  342.         for vloc := 0 to picheight - 1 do begin
  343.                 info := BigBufInfo;
  344.                 GetLine(hOffset, vloc + vOffset, picwidth, aLine);
  345.                 info := StackInfo;
  346.                 PutLine(0, vloc, picwidth, aLine);
  347.             end;
  348.         UpdatePicWindow;
  349.         info := BigBufInfo;
  350.     end;     {RegisterBackToSmallWindow}
  351.  
  352.  
  353. {******************************************************************************}
  354. {*     Find the angle which the current slice must be rotated in order to place it in register with the reference  *}
  355. {*  slice.  For corresponding pairs of fiducial points in the current and reference slices, use simple             *}
  356. {*  trigonometry to find the angle between lines passing through each pair of points.  Take an everage of the      *}
  357. {*  angles found from each set of corresponding pairs of points to find the rotation angle.                        *}
  358. {******************************************************************************}
  359.     function RegisterFindAngle (var fiducials: FidArray; Cur, Ref: integer): extended;
  360.         var
  361.             j, k, n: integer;
  362.             angle, anglecur, angleref: extended;
  363.             tancur, tanref, bfid: extended;
  364.             sumangle: extended;
  365.             b:array[1..9] of boolean;
  366.     begin
  367. {find average angle between current fiducial segments and reference fiducial segments}
  368.         sumangle := 0;
  369.         n := 0;
  370.         for j := 1 to MaxFids - 1 do
  371.             for k := j + 1 to MaxFids do begin
  372.                 bfid:=biggestFid; {ppc-bug}
  373.                 if (j <> k) and (fiducials[Cur, j].x < bfid) and (fiducials[Cur, j].y < bfid) and (fiducials[Cur, k].x < bfid)
  374.                 and (fiducials[Cur, k].y < bfid) and (fiducials[Ref, j].x < bfid) and (fiducials[Ref, j].y < bfid)
  375.                 and (fiducials[Ref, k].x < bfid) and (fiducials[Ref, k].y < bfid) then begin
  376.                         tanref := (fiducials[Ref, k].y - fiducials[Ref, j].y) / (fiducials[Ref, k].x - fiducials[Ref, j].x);
  377.                         if ((tanref > 0) and (fiducials[Ref, k].y - fiducials[Ref, j].y < 0)) or ((tanref < 0) and (fiducials[Ref, k].y - fiducials[Ref, j].y > 0)) then
  378.                             angleref := arctan(tanref) + pi
  379.                         else
  380.                             angleref := arctan(tanref);
  381.                         tancur := (fiducials[Cur, k].y - fiducials[Cur, j].y) / (fiducials[Cur, k].x - fiducials[Cur, j].x);
  382.                         if ((tancur > 0) and (fiducials[Cur, k].y - fiducials[Cur, j].y < 0)) or ((tancur < 0) and (fiducials[Cur, k].y - fiducials[Cur, j].y > 0)) then
  383.                             anglecur := arctan(tancur) + pi
  384.                         else
  385.                             anglecur := arctan(tancur);
  386.                         angle := anglecur - angleref;
  387.                         if (angle > pi) then
  388.                             angle := angle - 2 * pi;
  389.                         if (angle <= -pi) then
  390.                             angle := angle + 2 * pi;
  391.                         sumangle := sumangle + angle;
  392.                         n := n + 1;
  393.                     end;     {then}
  394.                 end;
  395.         if (n > 0) then
  396.             RegisterFindAngle := sumangle / n
  397.         else begin
  398.                 PutError('Insufficient fiducial data to calculate registration rotation.');
  399.                 RegisterFindAngle := 10000;
  400.             end;     {else}
  401.     end;     {function RegisterFindAngle}
  402.  
  403.  
  404. {*************************************}
  405. {*  Rotates the slice using ScaleAndRotate routine.     *}
  406. {*************************************}
  407.     procedure RegisterRotate (AngleInRadians: extended);
  408.     begin
  409.         rsHScale := 1.0;
  410.         rsVScale := 1.0;
  411.         rsAngle := (AngleInRadians / pi) * 180.0;
  412.         if info^.LutMode = ColorLut then
  413.             rsMethod := NearestNeighbor
  414.         else
  415.             rsMethod := Bilinear;
  416.         rsCreateNewWindow := false;
  417.         macro := true; {So ScaleAndRotate won't display its dialog box.}
  418.         ScaleAndRotate;
  419.         Macro := false;
  420.     end;
  421.  
  422.  
  423. {******************************************************************************}
  424. {*     Find the distances in x and y which the current slice must be translated in order to place it in register     *}
  425. {*  with the reference slice.                                                                                                                                      *}
  426. {******************************************************************************}
  427.     procedure FindTranslation (var xdelta, ydelta: integer; var fiducials: FidArray; i, RefPic: integer; xxscale, yyscale, angle: extended; xc, yc: RegisterRealArray; xcenterStage, ycenterStage: extended);
  428.         var
  429.             xcur, ycur: extended;
  430.             xdeltaindex: extended;     {used to detect non-linear mapping between coordinate systems of current}
  431.             ydeltaindex: extended;     {     and reference slices;  the closer to zero, the more linear the mapping}
  432.             xdeltamin, ydeltamin: extended; {minimize indices to find best fiducials for translation calculations}
  433.             j: integer;
  434.             bfid: extended;
  435.     begin
  436.         xdeltamin := biggestFid;
  437.         ydeltamin := biggestFid;
  438.         bfid := biggestFid;  {ppc-bug}
  439.         for j := 1 to MaxFids do
  440.             if (fiducials[i, j].x < bfid) and (fiducials[i, j].y < bfid) then begin
  441.                     xcur := fiducials[i, j].x - xc[i];
  442.                     ycur := fiducials[i, j].y - yc[i];
  443. {rotate original fiducials about screen center}
  444. {this changes values of first two parameters on return}
  445.                     RotateAboutXY(xcur, ycur, -angle, xcenterStage, ycenterStage);
  446. {calculate translation offsets}
  447.                     xdeltaindex := abs(fiducials[i, j].x - xc[i]) + abs(fiducials[RefPic, j].x - xc[RefPic]);
  448.                     if (xdeltaindex < xdeltamin) then begin     {try to minimize effect of warped tissue}
  449.                             xdelta := round(xxscale * (fiducials[RefPic, j].x - xc[RefPic] - xcur));
  450.                             xdeltamin := xdeltaindex;
  451.                         end;     {then}
  452.                     ydeltaindex := abs(fiducials[i, j].y - yc[i]) + abs(fiducials[RefPic, j].y - yc[RefPic]);
  453.                     if (ydeltaindex < ydeltamin) then begin     {try to minimize effect of warped tissue}
  454.                             ydelta := round(yyscale * (fiducials[RefPic, j].y - yc[RefPic] - ycur));
  455.                             ydeltamin := ydeltaindex;
  456.                         end;     {then}
  457.                 end;     {then}
  458.     end;     {procedure RegisterFindTranslation}
  459.  
  460.  
  461.  
  462. {******************************************************************************}
  463. {*     This procedure allows the user to determine, via radio buttons in a dialog box, whether fiducial data           *}
  464. {*  will be read from the screen or from a file.  The dialog box also contains details about how to select fiducial   *}
  465. {*  points on the screen.                                                                                                                                              *}
  466. {******************************************************************************}
  467.     function RegisterOptions: boolean;
  468.         var
  469.             mylog: Dialogptr;                                           {pointer to dialog box}
  470.             i, item, alldone: integer;
  471.             SaveFiducialMethod: FiducialMethodType;
  472.             SaveConfirmFidClicks: boolean;
  473.     begin
  474.         RegisterOptions := FALSE;
  475.         InitCursor;
  476.         SaveConfirmFidClicks := ConfirmFidClicks;
  477.         SaveFiducialMethod := FiducialMethod;
  478.         mylog := GetNewDialog(RegisterImagesID, nil, pointer(-1));
  479.         OutlineButton(MyLog, ok, 16);
  480.         SetDlogItem(MyLog, FiducialsOnScreenID, ord(FiducialMethod = OnScreen));
  481.         SetDlogItem(MyLog, FiducialsFromFileID, ord(FiducialMethod = FromFile));
  482.         SetDlogItem(MyLog, ConfirmFidClicksID, ord(ConfirmFidClicks));
  483.         alldone := 0;
  484.         repeat  {if we don't do it this way, ModalDialog throws us into code checking after each keystroke}
  485.             repeat   {meaning you can't type in a 2 digit number}
  486.                 ModalDialog(nil, item);
  487.                 if item = ConfirmFidClicksID then begin
  488.                         ConfirmFidClicks := not ConfirmFidClicks;
  489.                         SetDlogItem(MyLog, ConfirmFidClicksID, ord(ConfirmFidClicks));
  490.                     end;
  491.                 if (item = FiducialsOnScreenID) or (item = FiducialsFromFileID) then begin
  492.                         case item of
  493.                             FiducialsOnScreenID: 
  494.                                 FiducialMethod := OnScreen;
  495.                             FiducialsFromFileID: 
  496.                                 FiducialMethod := FromFile;
  497.                         end;     {case}
  498.                         SetDlogItem(MyLog, FiducialsOnScreenID, ord(FiducialMethod = OnScreen));
  499.                         SetDlogItem(MyLog, FiducialsFromFileID, ord(FiducialMethod = FromFile));
  500.                     end;
  501.             until (item = ok) or (item = cancel);
  502.             alldone := 1;
  503.         until (alldone = 1);
  504.         DisposeDialog(mylog);
  505.         if item = cancel then begin                             {if Cancel, keep the saved values}
  506.                 FiducialMethod := SaveFiducialMethod;
  507.                 ConfirmFidClicks := SaveConfirmFidClicks;
  508.                 Exit(RegisterOptions);
  509.             end;
  510.         RegisterOptions := TRUE;
  511.     end;
  512.  
  513.  
  514.  
  515. {******************************************************************************}
  516. {*     Place a set of slices in register with a reference slice using fiducial marks.  All slices in the stack              *}
  517. {*  are placed in register with the current slice using fiducial data gathered either from a text file or                 *}
  518. {*  from the user's mouse clicks on the screen.                                                                                                         *}
  519. {******************************************************************************}
  520.     procedure DoRegister;
  521.         var
  522.             nImages: integer;                                                     {total number of open slices to register}
  523.             RefImage: integer;                                                       {the index of the reference slice}
  524.             bigpicwidth, bigpicheight: longint;                        {width,height of big buffer used for rotation and translation}
  525.             picwidth, picheight: integer;                                  {width,height of slices to register}
  526.             xcenter, ycenter: integer;                                      {coordinates of center of big, temp window}
  527.             xdelta, ydelta: integer;                                            {translation offsets}
  528.             xcscreen, ycscreen: integer;                                  {image center on the screen}
  529.             xscreen1, yscreen1, xscreen2, yscreen2: integer;{two points in screen coordinates}
  530.             xstage1, ystage1, xstage2, ystage2: extended;              {same two points in stage coordinates}
  531.             xxscale, yyscale: extended;                                               {used for mapping stage to screen coords}
  532.             xc, yc: RegisterRealArray;                                    {array of image centers in stage coords}
  533.             fiducials: FidArray;                                                {array of fiducial point data for all slices}
  534.             xcenterStage, ycenterStage: extended;                          {used in translation calculation}
  535.             angle: extended;                                                             {mean angle between ref and cur fid segments}
  536.             i, ignore: integer;                                                   {loop indices and temp var}
  537.             TimeStr, seconds: str255;
  538.             StartTicks, TicksForOneRegistration, TicksToGo: LongInt;
  539.             StackInfo: InfoPtr;
  540.             SlicesDone: integer;
  541.  
  542.     begin
  543.         if not (RegisterOptions) then
  544.             exit(DoRegister);
  545.         with Info^ do begin
  546.                 picwidth := PixelsPerLine;
  547.                 picheight := nLines;
  548.                 RefImage := StackInfo^.CurrentSlice;
  549.                 nImages := StackInfo^.nSlices;
  550.             end;
  551.         if nImages > MaxRegSlices then begin
  552.                 PutError(concat('Unable to register more than ', long2str(MaxRegSlices), ' slices.'));
  553.                 exit(DoRegister);
  554.             end;
  555.         StackInfo := info;
  556.         bigpicwidth := 2 * (round(1.414 * picwidth) div 2);      {allow for image rotation without losing corners}
  557.         bigpicheight := 2 * (round(1.414 * picheight) div 2);    {odd window dims mysteriously don't work}
  558.         if (bigpicwidth * bigpicheight) > UndoBufSize then begin
  559.                 PutError(concat('To register this stack, the size of the Undo buffer must be increased to at least ', long2str(bigpicwidth * bigpicheight div 1024), 'K.'));
  560.                 exit(DoRegister);
  561.             end;
  562.         xcenter := bigpicwidth div 2;                                              {find center}
  563.         ycenter := bigpicheight div 2;
  564. {open fiducial data file}
  565. {read fiducial marks and image centers into arrays; RegPic is image reference}
  566. {get screen to stage coordinate mapping}
  567.         case FiducialMethod of
  568.             OnScreen: 
  569.                 if not GetFiducialDataFromScreen(xcscreen, ycscreen, xscreen1, yscreen1, xscreen2, yscreen2, xstage1, ystage1, xstage2, ystage2, fiducials, xc, yc) then
  570.                     exit(DoRegister);
  571.             FromFile: 
  572.                 if not GetFiducialDataFromFile(xcscreen, ycscreen, xscreen1, yscreen1, xscreen2, yscreen2, xstage1, ystage1, xstage2, ystage2, fiducials, xc, yc) then
  573.                     exit(DoRegister)
  574.         end;     {case}
  575.         xxscale := (xscreen2 - xscreen1) / (xstage2 - xstage1);
  576.         yyscale := (yscreen2 - yscreen1) / (ystage2 - ystage1);
  577.         xcscreen := xcscreen + (bigpicwidth - picwidth) div 2;     {adjust for bigger window}
  578.         ycscreen := ycscreen + (bigpicheight - picheight) div 2;
  579.         xcenterStage := (xcenter - xcscreen) / xxscale;
  580.         ycenterStage := (ycenter - ycscreen) / yyscale;
  581.         UpdatePicWindow;
  582.         ShowWatch;
  583.         i := 1;
  584.         if not NewPicWindow('Temp', bigpicwidth, bigpicheight) then
  585.             exit(DoRegister);
  586.         ShowMessage(CmdPeriodToStop);
  587.         SlicesDone := 1; {Don't need to process reference slice}
  588.         while (i <= nImages) and not CommandPeriod do begin
  589.                 if i = RefImage then begin
  590.                         i := i + 1;
  591.                         if i > nImages then
  592.                             leave;
  593.                     end;
  594.                 StartTicks := TickCount;
  595.                 with info^ do
  596.                     SetWTitle(wptr, concat('Temp (', long2str(i), '/', long2str(nImages), ')'));
  597.         {rotate image then translate to complete registration}
  598.                 angle := RegisterFindAngle(fiducials, i, RefImage);
  599.                 if (angle > 9999) then
  600.                     leave;
  601.                 CenterInBigBuffer(i, picwidth, picheight, bigpicwidth, bigpicheight, StackInfo);
  602.                 if (abs(angle) > 0.0001) then
  603.                     RegisterRotate(angle);
  604.                 if CommandPeriod then
  605.                     leave;
  606.                 FindTranslation(xdelta, ydelta, fiducials, i, RefImage, xxscale, yyscale, angle, xc, yc, xcenterStage, ycenterStage);
  607.                 TranslateBackToStack(i, xdelta, ydelta, picwidth, picheight, bigpicwidth, bigpicheight, StackInfo);
  608.                 SlicesDone := SlicesDone + 1;
  609.                 TicksForOneRegistration := TickCount - StartTicks;
  610.                 TicksToGo := (nImages - SlicesDone) * TicksForOneRegistration;
  611.                 NumToString((TicksToGo div 60) mod 60, seconds);
  612.                 if length(seconds) = 1 then
  613.                     seconds := concat('0', seconds);
  614.                 timestr := concat(long2str(TicksToGo div 3600), ':', seconds);
  615.                 ShowMessage(concat(CmdPeriodToStop, crStr, 'time: ', timestr));
  616.                 i := i + 1;
  617.             end;     {while i}
  618.         Info^.changes := false;
  619.         ignore := CloseAWindow(info^.wptr);
  620.         info := StackInfo;
  621.         if CommandPeriod then
  622.             ShowMessage('Registration cancelled.')
  623.         else begin
  624.                 SetSlice(RefImage);  {select registered slice as current slice}
  625.                 UpdatePicWindow;
  626.                 info^.changes := true;
  627.                 ShowMessage('Registration successful.');
  628.             end;     {else}
  629.     end;
  630.  
  631.  
  632.  
  633. end.